import * as THREE from "three";
import {
  latLngToXYZ,
  rectToPolygon,
  createLine,
  clearLines,
  getEarthScreenDiameterPrecise,
  getVisibleArcBySampling,
  getMouseRaycast,
  getMouseRaycastForModel,
  initEarthScene,
  polygonIntersectsRect,
  rectIntersects,
  pointIntersectsRect,
  latLngToModelSurface,
  getAllMeshes,
  createMarker,
  getCorrectLngRange,
} from "../utils/earthUtils";

import { LatLngPoint, LatLngPolygon, LatLngRect, DataAreaFile, LocationPoint, DetectionPoint } from "../types/earth";

interface EarthGlobeOptions {
  radius: number;
  modelUrl?: string;
  locationPoints?: LocationPoint[];
  detectionPoints?: DetectionPoint[]; // 新增探测点数据
  selectionActive?: boolean;
  onSelectionEnd?: (
    selectionRect: LatLngRect | null,
    intersectingRects: LatLngRect[],
    intersectingDetectionPoints: DetectionPoint[]
  ) => void; // 选区结束回调
  onSelectionChange?: (selectionRect: LatLngRect | null) => void; // 选区变化回调
  onHover?: (coord: LatLngPoint | null) => void;
  onClick?: (coord: LatLngPoint | null) => void;
  onLocationHover?: (location: LocationPoint | null) => void;
  onLocationClick?: (location: LocationPoint | null) => void;
  onDetectionPointHover?: (detectionPoint: DetectionPoint | null) => void; // 新增探测点悬停回调
  onDetectionPointClick?: (detectionPoint: DetectionPoint | null) => void; // 新增探测点点击回调
  onPatrolAreaClick?: (patrolArea: LatLngRect | null) => void; // 新增巡视区点击回调
  onPatrolAreaHover?: (patrolArea: LatLngRect | null) => void; // 新增巡视区悬停回调
  isSphere?: boolean; // 是否为球体
  tileUrlTemplate?: string; // 瓦片URL模版
  minZoom?: number;
  maxZoom?: number;
  onZoomChange?: (z: number, level: number) => void; // 缩放变化回调
  debug?: boolean; // debug模式，显示纯色mesh
  simulatedMetaKey?: boolean; // 模拟Meta键状态
}

export class EarthGlobe {
  private container: HTMLDivElement;
  private renderer: THREE.WebGLRenderer;
  private scene: THREE.Scene;
  private camera: THREE.PerspectiveCamera;
  private earthMesh: THREE.Mesh;
  private animationId: number = 0;
  private isDragging = false;
  private hasDragged = false;
  private lastMouse = { x: 0, y: 0 };
  private lastRotation = { x: 0, y: 0 };
  private dragStart = { x: 0, y: 0 };
  private inertia = { vx: 0, vy: 0 };
  private lastMove = { x: 0, y: 0, t: 0 };
  private DRAG_CLICK_BUFFER = 5;
  private eventsBound = false; // 跟踪事件绑定状态
  private radius: number;
  private polygons: LatLngPolygon[] = [];
  private rects: LatLngRect[] = [];
  private patrolAreas: LatLngRect[] = []; // 新增巡视区数据
  private locationPoints: LocationPoint[] = [];
  private detectionPoints: DetectionPoint[] = []; // 新增探测点数据
  private polygonLines: THREE.Line[] = [];
  private textMeshes: THREE.Mesh[] = [];
  private locationMarkers: THREE.Mesh[] = [];
  private locationLabels: THREE.Mesh[] = [];
  private detectionPointLabels: THREE.Object3D[] = []; // 新增探测点标签
  private detectionPointLines: THREE.Line[] = []; // 新增探测点连线
  private patrolAreaLines: THREE.Line[] = []; // 新增巡视区线条
  private patrolAreaLabels: THREE.Sprite[] = []; // 新增巡视区标签
  private selectionPoints: [LatLngPoint, LatLngPoint] | null = null;
  private selectionActive: boolean = false;
  private selectionLine: THREE.Line | null = null;
  // 删除markerPoint、markerMesh属性
  private selectionClickCount: number = 0;
  private externalSelectionActive: boolean = false;
  private intersectedPolygons: boolean[] = [];
  private intersectedDetectionPoints: boolean[] = []; // 新增相交探测点状态
  private onSelectionEnd?: (
    selectionRect: LatLngRect | null,
    intersectingRects: LatLngRect[],
    intersectingDetectionPoints: DetectionPoint[]
  ) => void; // 选区结束回调
  private onSelectionChange?: (selectionRect: LatLngRect | null) => void; // 选区变化回调
  private onHover?: (coord: LatLngPoint | null) => void;
  private onClick?: (coord: LatLngPoint | null) => void;
  private onLocationHover?: (location: LocationPoint | null) => void;
  private onLocationClick?: (location: LocationPoint | null) => void;
  private onDetectionPointHover?: (detectionPoint: DetectionPoint | null) => void; // 新增探测点悬停回调
  private onDetectionPointClick?: (detectionPoint: DetectionPoint | null) => void; // 新增探测点点击回调
  private onPatrolAreaClick?: (patrolArea: LatLngRect | null) => void; // 新增巡视区点击回调
  private onPatrolAreaHover?: (patrolArea: LatLngRect | null) => void; // 新增巡视区悬停回调
  private modelUrl?: string;
  private isSphere: boolean = true; // 新增
  private tileMeshes: Map<string, THREE.Mesh> = new Map();
  private level1TileMesh: THREE.Mesh | null = null;
  private tileUrlTemplate?: string;
  private minZoom: number = 1;
  private maxZoom: number = 5;
  private onZoomChange?: (z: number, level: number) => void;
  private simulatedMetaKey: boolean = false; // 模拟Meta键状态
  // EarthGlobe类属性
  private visibleTileKeys: string[] = [];
  // 纹理缓存
  private textureCache: Map<string, THREE.Texture> = new Map();
  // 纹理Promise缓存，防止重复加载
  private tileTexturePromiseCache: Map<string, Promise<THREE.Texture>> = new Map();
  // 失败瓦片缓存，避免重复尝试加载失败的瓦片
  private failedTileCache: Set<string> = new Set();
  // 场景版本号，保证异步加载完成时场景未变
  private sceneVersion: number = 0;
  private currentZoomLevel: number = 1; // 添加当前缩放级别跟踪

  // 释放mesh资源
  private disposeMesh(mesh: THREE.Mesh) {
    if (mesh.geometry) {
      mesh.geometry.dispose();
    }
    if (Array.isArray(mesh.material)) {
      mesh.material.forEach((material) => {
        if (material.map) {
          material.map.dispose();
        }
        material.dispose();
      });
    } else if (mesh.material) {
      if (mesh.material.map) {
        mesh.material.map.dispose();
      }
      mesh.material.dispose();
    }
  }

  constructor(container: HTMLDivElement, options: EarthGlobeOptions) {
    this.container = container;
    this.radius = options.radius;
    this.modelUrl = options.modelUrl;
    this.locationPoints = options.locationPoints || [];
    this.detectionPoints = options.detectionPoints || []; // 新增探测点数据
    this.externalSelectionActive = !!options.selectionActive;
    this.onSelectionEnd = options.onSelectionEnd;
    this.onSelectionChange = options.onSelectionChange; // 新增：初始化选区变化回调
    this.onHover = options.onHover;
    this.onClick = options.onClick;
    this.onLocationHover = options.onLocationHover;
    this.onLocationClick = options.onLocationClick;
    this.onDetectionPointHover = options.onDetectionPointHover; // 新增探测点悬停回调
    this.onDetectionPointClick = options.onDetectionPointClick; // 新增探测点点击回调
    this.onPatrolAreaClick = options.onPatrolAreaClick; // 新增巡视区点击回调
    this.onPatrolAreaHover = options.onPatrolAreaHover; // 新增巡视区悬停回调
    this.isSphere = typeof options.isSphere === "boolean" ? options.isSphere : true; // 默认true，后续自动判断
    this.tileUrlTemplate = options.tileUrlTemplate;
    this.minZoom = options.minZoom ?? 1;
    this.maxZoom = options.maxZoom ?? 5;
    this.onZoomChange = options.onZoomChange;
    this.simulatedMetaKey = options.simulatedMetaKey ?? false;

    // 初始化 Three.js 场景
    const { scene, camera, renderer, earthMesh } = initEarthScene({
      width: container.clientWidth || window.innerWidth,
      height: container.clientHeight || window.innerHeight,
      modelUrl: this.modelUrl,
      onModelLoaded: (mesh) => {
        this.earthMesh = mesh;
        // 设置地球默认朝向经度0度（格林威治子午线）
        mesh.rotation.x = 0;
        mesh.rotation.y = -Math.PI / 2; // -90度，让地球朝向经度0度
        // 自动判断是否为球体
        if (typeof options.isSphere !== "boolean") {
          // 只有当未手动指定时才自动判断
          if ((mesh as any).geometry && (mesh as any).geometry.type === "SphereGeometry") {
            this.isSphere = true;
          } else {
            this.isSphere = false;
          }
        }
        // earthMesh初始化后立即渲染rects/polygons和地点标记
        if ((this.rects && this.rects.length > 0) || (this.polygons && this.polygons.length > 0)) {
          this.renderPolygons();
        }
        if (this.locationPoints && this.locationPoints.length > 0) {
          this.renderLocationPoints();
        }
        if (this.detectionPoints && this.detectionPoints.length > 0) {
          this.renderDetectionPoints();
        }
      },
    });
    this.scene = scene;
    this.camera = camera;
    this.renderer = renderer;
    this.earthMesh = earthMesh;

    this.container.appendChild(this.renderer.domElement);
    this.setRendererSize();
    this.bindEvents();

    // 立即加载level1瓦片作为基础底图
    if (this.isSphere && this.tileUrlTemplate) {
      this.loadLevel1Tile();
    }

    this.animate();
  }

  private setRendererSize() {
    const width = this.container.clientWidth;
    const height = this.container.clientHeight;
    this.renderer.setSize(width, height, false);
    this.camera.aspect = width / height;
    this.camera.updateProjectionMatrix();
    this.renderer.domElement.style.width = width + "px";
    this.renderer.domElement.style.height = height + "px";
  }

  private bindEvents() {
    this.renderer.domElement.addEventListener("mousedown", this.onMouseDown);
    this.renderer.domElement.addEventListener("wheel", this.onWheel, { passive: false });
    window.addEventListener("resize", this.onResize);

    // 添加鼠标进入和离开事件来更新光标样式
    this.renderer.domElement.addEventListener("mouseenter", this.onMouseEnter);
    this.renderer.domElement.addEventListener("mouseleave", this.onMouseLeave);
    
    // 绑定独立的悬停事件
    this.bindHoverEvents();
  }

  private unbindEvents() {
    this.renderer.domElement.removeEventListener("mousedown", this.onMouseDown);
    this.renderer.domElement.removeEventListener("wheel", this.onWheel);
    window.removeEventListener("resize", this.onResize);

    // 移除鼠标进入和离开事件监听器
    this.renderer.domElement.removeEventListener("mouseenter", this.onMouseEnter);
    this.renderer.domElement.removeEventListener("mouseleave", this.onMouseLeave);

    // 确保清理可能存在的动态绑定事件
    this.unbindMouseMoveEvents();
    
    // 解绑悬停事件
    this.unbindHoverEvents();
  }

  // 绑定鼠标移动事件
  private bindMouseMoveEvents() {
    if (!this.eventsBound) {
      window.addEventListener("mousemove", this.onMouseMove);
      window.addEventListener("mouseup", this.onMouseUp);
      this.eventsBound = true;
    }
  }

  // 解绑鼠标移动事件
  private unbindMouseMoveEvents() {
    if (this.eventsBound) {
      window.removeEventListener("mousemove", this.onMouseMove);
      window.removeEventListener("mouseup", this.onMouseUp);
      this.eventsBound = false;
    }
  }

  // 绑定悬停事件
  private bindHoverEvents() {
    this.renderer.domElement.addEventListener("mousemove", this.onHoverMove);
  }

  // 解绑悬停事件
  private unbindHoverEvents() {
    this.renderer.domElement.removeEventListener("mousemove", this.onHoverMove);
  }

  private onMouseDown = (event: MouseEvent) => {
    const selectionMode = event.metaKey || this.externalSelectionActive || this.simulatedMetaKey;
    if (selectionMode) {
      // 选区模式下，第一次点后移动鼠标实时渲染选区和相交高亮
      this.bindMouseMoveEvents();
      return;
    }

    this.isDragging = true;
    this.hasDragged = false;
    this.lastMouse = { x: event.clientX, y: event.clientY };
    this.dragStart = { x: event.clientX, y: event.clientY };
    this.lastRotation = {
      x: this.earthMesh.rotation.x,
      y: this.earthMesh.rotation.y,
    };
    this.lastMove = { x: event.clientX, y: event.clientY, t: Date.now() };
    this.inertia = { vx: 0, vy: 0 };

    // 开始拖拽时动态绑定mousemove和mouseup事件（避免重复绑定）
    this.bindMouseMoveEvents();
  };

  // 独立的悬停事件处理
  private onHoverMove = (event: MouseEvent) => {
    if (!this.earthMesh || !this.camera || !this.renderer) {
      return;
    }

    // 如果正在拖拽或选区模式下，不处理悬停
    if (this.isDragging || this.selectionActive) {
      return;
    }

    // 悬停坐标
    if (this.onHover) {
      const hit = this.getMouseLatLng(event);
      this.onHover(hit);
    }

    // 检测地点标记悬停
    if (this.onLocationHover) {
      const hoveredLocation = this.getLocationAtPoint(event);
      this.onLocationHover(hoveredLocation);
    }

    // 检测探测点悬停
    if (this.onDetectionPointHover) {
      const hoveredDetectionPoint = this.getDetectionPointAtPoint(event);
      this.onDetectionPointHover(hoveredDetectionPoint);
    }

    // 检测巡视区悬停
    if (this.onPatrolAreaHover) {
      const hoveredPatrolArea = this.getPatrolAreaAtPoint(event);
      this.onPatrolAreaHover(hoveredPatrolArea);
    }
  };

  private onMouseMove = (event: MouseEvent) => {
    if (!this.earthMesh || !this.camera || !this.renderer) {
      return;
    }

    // 选区模式下，第一次点后移动鼠标实时渲染选区和相交高亮
    if (this.selectionActive && this.selectionPoints && this.selectionClickCount === 1) {
      const hit = this.getMouseLatLng(event);
      if (hit) {
        this.selectionPoints = [this.selectionPoints[0], hit];
        this.updateIntersectedPolygons();
        this.renderSelection();
        this.renderPolygons();
        // 实时显示选区坐标信息
        this.displaySelectionCoordinates();
        // 实时更新选区状态
        if (this.onSelectionChange && this.selectionPoints) {
          const [start, end] = this.selectionPoints;
          const lngRange = getCorrectLngRange(start.lng, end.lng);
          const selectionRect: LatLngRect = {
            minLat: Math.min(start.lat, end.lat),
            maxLat: Math.max(start.lat, end.lat),
            minLng: lngRange.minLng,
            maxLng: lngRange.maxLng,
          };
          this.onSelectionChange(selectionRect);
        }
      }
      return;
    }

    // 拖动判定
    if (this.isDragging) {
      const dx = event.clientX - this.lastMouse.x;
      const dy = event.clientY - this.lastMouse.y;
      if (!this.hasDragged) {
        const totalDx = event.clientX - this.dragStart.x;
        const totalDy = event.clientY - this.dragStart.y;
        const dist = Math.sqrt(totalDx * totalDx + totalDy * totalDy);
        if (dist > this.DRAG_CLICK_BUFFER) {
          this.hasDragged = true;
        }
      }
      if (this.hasDragged) {
        // 只处理地球旋转
        const earthScreenDiameter = getEarthScreenDiameterPrecise(this.camera, this.renderer);
        const visibleArc = getVisibleArcBySampling(this.camera, this.renderer);
        const deltaTheta = (dx / earthScreenDiameter) * visibleArc;
        const deltaPhi = (dy / earthScreenDiameter) * visibleArc;
        this.earthMesh.rotation.y = this.lastRotation.y + deltaTheta;
        this.earthMesh.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, this.lastRotation.x + deltaPhi));
        // 记录速度
        const now = Date.now();
        const dt = now - this.lastMove.t;
        if (dt > 0) {
          this.inertia = {
            vx: (event.clientX - this.lastMove.x) / dt,
            vy: (event.clientY - this.lastMove.y) / dt,
          };
        }
        this.lastMove = { x: event.clientX, y: event.clientY, t: now };
        return;
      }
    }
  };

  private onMouseUp = (event: MouseEvent) => {

    // click 逻辑
    // 选区模式：meta键、外部参数或模拟Meta键
    const selectionMode = event.metaKey || this.externalSelectionActive || this.simulatedMetaKey;
    if (selectionMode) {
      const hit = this.getMouseLatLng(event);
      if (hit) {
        if (this.selectionClickCount === 0) {
          this.selectionPoints = [hit, hit];
          this.selectionActive = true;
          this.selectionClickCount = 1;
          this.renderSelection();
          // 清除之前的选区坐标显示
          this.hideSelectionCoordinates();
          // 显示选区坐标
          this.displaySelectionCoordinates();
        } else if (this.selectionClickCount === 1 && this.selectionPoints) {
          this.selectionPoints = [this.selectionPoints[0], hit];
          this.selectionActive = false;
          this.selectionClickCount = 0;
          this.updateIntersectedPolygons();
          this.renderSelection();
          this.renderPolygons();
          // 选区结束时回调
          if (this.onSelectionEnd && this.selectionPoints) {
            const [start, end] = this.selectionPoints;
            const lngRange = getCorrectLngRange(start.lng, end.lng);
            const selectionRect: LatLngRect = {
              minLat: Math.min(start.lat, end.lat),
              maxLat: Math.max(start.lat, end.lat),
              minLng: lngRange.minLng,
              maxLng: lngRange.maxLng,
            };

            // 查找与选区相交的rects
            const intersectingRects = this.rects.filter((rect) => rectIntersects(rect, selectionRect));

            // 查找与选区相交的探测点
            const intersectingDetectionPoints = this.detectionPoints.filter((point) => {
              return pointIntersectsRect({ lat: point.center_latitude, lng: point.center_longitude }, selectionRect);
            });

            this.onSelectionEnd(selectionRect, intersectingRects, intersectingDetectionPoints);
          }
          // 保留选区坐标显示，不清除
          this.displaySelectionCoordinates();
          
          // 选区完成，解绑事件
          this.unbindMouseMoveEvents();
        }
      }
      return;
    }
    
    this.unbindMouseMoveEvents();
    
    if (this.isDragging) {
      this.isDragging = false;

      const dx = event.clientX - this.dragStart.x;
      const dy = event.clientY - this.dragStart.y;
      const dist = Math.sqrt(dx * dx + dy * dy);
      if (dist > this.DRAG_CLICK_BUFFER) {
        return;
      }
    }

    // 标记逻辑
    const hit = this.getMouseLatLng(event);
    if (hit) {
      // 删除所有this.setMarker、this.renderMarker相关调用
    }
    // 点击坐标回调
    if (this.onClick) {
      this.onClick(hit);
    }

    // 检测巡视区点击
    if (this.onPatrolAreaClick) {
      const clickedPatrolArea = this.getPatrolAreaAtPoint(event);
      if (clickedPatrolArea) {
        this.onPatrolAreaClick(clickedPatrolArea);
        return; // 如果点击了巡视区，不触发其他点击
      }
    }

    // 检测探测点点击
    if (this.onDetectionPointClick) {
      const clickedDetectionPoint = this.getDetectionPointAtPoint(event);
      if (clickedDetectionPoint) {
        this.onDetectionPointClick(clickedDetectionPoint);
        return; // 如果点击了探测点，不触发其他点击
      }
    }

    // 检测地点标记点击
    if (this.onLocationClick) {
      const clickedLocation = this.getLocationAtPoint(event);
      if (clickedLocation) {
        this.onLocationClick(clickedLocation);
        return; // 如果点击了地点标记，不触发地图坐标点击
      }
    }
  };

  private onMouseEnter = () => {
    this.updateCursorStyle();
  };

  private onMouseLeave = () => {
    this.renderer.domElement.style.cursor = "default";
  };

  private updateCursorStyle = () => {
    const selectionMode = this.externalSelectionActive || this.simulatedMetaKey;
    if (selectionMode) {
      this.renderer.domElement.style.cursor = "crosshair";
    } else {
      this.renderer.domElement.style.cursor = "grab";
    }
  };

  private onWheel = (event: WheelEvent) => {
    event.preventDefault();
    const zoomSpeed = 0.1;
    this.camera.position.z += event.deltaY * zoomSpeed * 0.01;
    this.camera.position.z = Math.max(1.05, Math.min(30, this.camera.position.z));
  };

  private onResize = () => {
    this.setRendererSize();
  };

  private animate = () => {
    this.animationId = requestAnimationFrame(this.animate);
    if (!this.isDragging && this.earthMesh) {
      const base = 0.008;
      const z = this.camera.position.z || 3;
      let sensitivity = base * (z - 1);
      sensitivity = Math.max(sensitivity, 0.0008);
      this.earthMesh.rotation.y += this.inertia.vx * sensitivity * 16;
      this.earthMesh.rotation.x = Math.max(
        -Math.PI / 2,
        Math.min(Math.PI / 2, this.earthMesh.rotation.x + this.inertia.vy * sensitivity * 16)
      );
      this.inertia.vx *= 0.92;
      this.inertia.vy *= 0.92;
      if (Math.abs(this.inertia.vx) < 0.001) {
        this.inertia.vx = 0;
      }
      if (Math.abs(this.inertia.vy) < 0.001) {
        this.inertia.vy = 0;
      }
    }
    // 球体tile加载
    if (this.isSphere && this.tileUrlTemplate) {
      this.updateTiles();
    }
    // 每帧回调z和level
    if (this.onZoomChange) {
      const z = this.camera.position.z || 3;
      const level = this.getZoomLevel();
      this.onZoomChange(z, level);
    }

    // 检测级别变化，如果级别变化了，重新渲染location
    const currentLevel = this.getZoomLevel();
    if (currentLevel !== this.currentZoomLevel) {
      this.currentZoomLevel = currentLevel;
      this.renderLocationPoints();
    }
    // 更新所有标签的位置和大小，实现真正的屏幕空间固定大小
    const updateLabel = (object: THREE.Object3D, lat: number, lng: number) => {
      // 计算文字在球体表面的初始位置
      const labelOffset = latLngToXYZ(lat, lng, 1.001);

      // 使用applyEuler计算文字在球体旋转后的位置
      const worldPos = labelOffset.clone().applyEuler(this.earthMesh.rotation);
      object.position.copy(worldPos);

      // 用邻边（depth）来修正投影计算
      const camDir = new THREE.Vector3();
      this.camera.getWorldDirection(camDir);

      const camToObject = new THREE.Vector3().subVectors(object.position, this.camera.position);

      // 计算光轴方向的深度 (邻边)
      const depth = camToObject.dot(camDir);

      // 用邻边算 frustum 高度
      const frustumHeight = 2 * depth * Math.tan((this.camera.fov * Math.PI) / 180 / 2);
      const worldPerPixel = frustumHeight / this.renderer.domElement.clientHeight;

      // 设置缩放，让Sprite在屏幕上保持固定像素大小
      if (object instanceof THREE.Sprite && object.material.map) {
        const canvasWidth = object.material.map.image.width;
        const canvasHeight = object.material.map.image.height;

        // 根据对象类型设置不同的目标像素大小
        let desiredPixelSize = 100; // 默认标签大小

        // 如果是探测点圆心标记，使用更小的尺寸
        if (object.userData.detectionPoint && object.userData.isCircleMarker) {
          desiredPixelSize = 16; // 探测点圆心大小
        } else if (object.userData.detectionPoint && object.userData.isNumberLabel) {
          desiredPixelSize = 20; // 探测点序号大小
        } else if (object.userData.detectionPoint && object.userData.isNameLabel) {
          desiredPixelSize = 100; // 探测点名称大小，增大到120像素
        } else if (object.userData.isPatrolArea) {
          desiredPixelSize = canvasWidth / 2; // 巡视区标签大小
        } else if (object.userData.location) {
          // 对于location label，根据canvas宽度动态调整目标像素大小
          // 保持文字高度一致，宽度根据内容调整
          const baseHeight = 28; // 基础字体高度
          const targetHeight = 14; // 目标显示高度（像素）
          const heightRatio = targetHeight / baseHeight;
          desiredPixelSize = canvasWidth * heightRatio;
        }

        const worldSize = desiredPixelSize * worldPerPixel;
        const scaleX = worldSize;
        const scaleY = worldSize * (canvasHeight / canvasWidth);
        object.scale.set(scaleX, scaleY, 1);
      }

      // 为探测点label添加屏幕偏移，让它在右上方显示
      if (object.userData.isDetectionPointLabel) {
        // 计算屏幕偏移
        const screenOffsetX = 12 * worldPerPixel; // 向右偏移10像素
        const screenOffsetY = 10 * worldPerPixel; // 向上偏移10像素

        // 计算相机右向量和上向量
        const cameraRight = new THREE.Vector3();
        const cameraUp = new THREE.Vector3();
        this.camera.getWorldDirection(camDir);
        cameraRight.setFromMatrixColumn(this.camera.matrix, 0);
        cameraUp.setFromMatrixColumn(this.camera.matrix, 1);

        // 应用屏幕偏移
        object.position.add(cameraRight.multiplyScalar(screenOffsetX));
        object.position.add(cameraUp.multiplyScalar(screenOffsetY));
      }

      // 检查标签是否在球体的可见面上（朝向相机的一面）
      const cameraToObject = new THREE.Vector3().subVectors(object.position, this.camera.position);
      const objectNormal = object.position.clone().normalize();
      const dotProduct = cameraToObject.dot(objectNormal);
      object.visible = dotProduct < 0;
    };

    // 更新矩形区域标签
    this.textMeshes.forEach((sprite, index) => {
      if (index < this.rects.length) {
        const rect = this.rects[index];
        const centerLat = (rect.minLat + rect.maxLat) / 2;
        const centerLng = (rect.minLng + rect.maxLng) / 2;
        updateLabel(sprite, centerLat, centerLng);
      }
    });

    // 更新地点标签
    this.locationLabels.forEach((label) => {
      const location = label.userData.location;
      if (location && location.center_latitude !== undefined) {
        updateLabel(label, location.center_latitude, location.center_longitude);
      }
    });

    // 更新探测点标签
    this.detectionPointLabels.forEach((label) => {
      const detectionPoint = label.userData.detectionPoint;
      if (detectionPoint && detectionPoint.latitude !== undefined) {
        updateLabel(label, detectionPoint.latitude, detectionPoint.longitude);
      }
    });

    // 更新巡视区标签
    this.patrolAreaLabels.forEach((label) => {
      const area = label.userData.area;
      if (area) {
        const centerLat = (area.minLat + area.maxLat) / 2;
        const centerLng = (area.minLng + area.maxLng) / 2;
        updateLabel(label, centerLat, centerLng);
      }
    });

    // 更新探测点连线位置
    this.detectionPointLines.forEach((line, index) => {
      if (index < this.detectionPoints.length - 1) {
        const point1 = this.detectionPoints[index];
        const point2 = this.detectionPoints[index + 1];

        // 重新创建大圆弧线，使用稍高的半径避免被遮挡
        const points = this.createGreatCircleArc(
          point1.center_latitude,
          point1.center_longitude,
          point2.center_latitude,
          point2.center_longitude,
          1.001
        );

        // 应用地球旋转
        const rotatedPoints = points.map((point) => point.clone().applyEuler(this.earthMesh.rotation));

        // 更新几何体
        line.geometry.setFromPoints(rotatedPoints);
        line.geometry.attributes.position.needsUpdate = true;

        // 检查连线是否在球体的可见面上（朝向相机的一面）
        if (rotatedPoints.length > 0) {
          const lineCenter = new THREE.Vector3();
          // 计算连线的中心点
          rotatedPoints.forEach((point) => lineCenter.add(point));
          lineCenter.divideScalar(rotatedPoints.length);

          const cameraToLine = new THREE.Vector3().subVectors(lineCenter, this.camera.position);
          const lineNormal = lineCenter.clone().normalize();
          const dotProduct = cameraToLine.dot(lineNormal);

          // 如果连线在球体背面，则隐藏
          line.visible = dotProduct < 0;
        }
      }
    });

    this.renderer.render(this.scene, this.camera);
  };

  public dispose() {
    this.unbindEvents();

    // 清理瓦片资源
    for (const [key, mesh] of this.tileMeshes) {
      this.earthMesh.remove(mesh);
      this.disposeMesh(mesh);
    }
    this.tileMeshes.clear();

    // 清理一级瓦片
    if (this.level1TileMesh) {
      this.earthMesh.remove(this.level1TileMesh);
      this.disposeMesh(this.level1TileMesh);
      this.level1TileMesh = null;
    }

    // 清理纹理缓存
    for (const texture of this.textureCache.values()) {
      texture.dispose();
    }
    this.textureCache.clear();
    
    // 清理失败缓存
    this.failedTileCache.clear();

    // 清理多边形线框
    if (this.earthMesh) {
      clearLines(this.polygonLines, this.earthMesh);
    }

    // 清理文本标签
    this.textMeshes.forEach((mesh) => {
      this.scene.remove(mesh);
      if (mesh.geometry) mesh.geometry.dispose();
      if (mesh.material) {
        if (mesh.material.map) mesh.material.map.dispose();
        mesh.material.dispose();
      }
    });
    this.textMeshes = [];

    // 清理选择线
    if (this.selectionLine && this.earthMesh) {
      this.earthMesh.remove(this.selectionLine);
      this.selectionLine.geometry.dispose();
      if (Array.isArray(this.selectionLine.material)) {
        this.selectionLine.material.forEach((m) => m.dispose());
      } else {
        this.selectionLine.material.dispose();
      }
    }

    // 清理地点标签
    this.locationLabels.forEach((label) => {
      this.scene.remove(label);
      if (label.material) {
        if (label.material.map) label.material.map.dispose();
        label.material.dispose();
      }
    });

    // 清理探测点标签
    this.detectionPointLabels.forEach((label) => {
      this.scene.remove(label);
      if (label.material) {
        if (label.material.map) label.material.map.dispose();
        label.material.dispose();
      }
    });

    // 清理探测点连线
    this.detectionPointLines.forEach((line) => {
      this.scene.remove(line);
      if (line.geometry) line.geometry.dispose();
      if (line.material) line.material.dispose();
    });
    this.locationLabels = [];

    // 清理地球材质和纹理
    if (this.earthMesh) {
      if (Array.isArray(this.earthMesh.material)) {
        this.earthMesh.material.forEach((material) => {
          if (material.map) {
            material.map.dispose();
          }
          material.dispose();
        });
      } else if (this.earthMesh.material) {
        if (this.earthMesh.material.map) {
          this.earthMesh.material.map.dispose();
        }
        this.earthMesh.material.dispose();
      }
    }

    if (this.container && this.renderer.domElement.parentNode === this.container) {
      this.container.removeChild(this.renderer.domElement);
    }
    this.renderer.dispose();
    cancelAnimationFrame(this.animationId);
  }

  public setPolygons(polygons: LatLngPolygon[]) {
    this.polygons = polygons || [];
    this.renderPolygons();
  }

  public setRects(rects: LatLngRect[]) {
    this.rects = rects || [];
    this.renderPolygons();
  }

  public setPatrolAreas(patrolAreas: LatLngRect[]) {
    this.patrolAreas = patrolAreas || [];
    this.renderPatrolAreas();
  }

  public setLocationPoints(locationPoints: LocationPoint[]) {
    this.locationPoints = locationPoints || [];
    this.renderLocationPoints();
  }

  public setDetectionPoints(detectionPoints: DetectionPoint[]) {
    this.detectionPoints = detectionPoints || [];
    this.intersectedDetectionPoints = new Array(this.detectionPoints.length).fill(false);
    this.renderDetectionPoints();
  }

  private renderPatrolAreas() {
    if (!this.earthMesh) {
      return;
    }
    
    // 清理旧的巡视区线条
    this.patrolAreaLines?.forEach((line) => {
      this.earthMesh?.remove(line);
    });
    this.patrolAreaLines = [];

    // 清理旧的巡视区文本标签
    this.patrolAreaLabels?.forEach((sprite) => {
      this.scene.remove(sprite);
      if (sprite.material) {
        if (sprite.material.map) sprite.material.map.dispose();
        sprite.material.dispose();
      }
    });
    this.patrolAreaLabels = [];

    // 将巡视区转换为多边形并绘制
    this.patrolAreas.forEach((area) => {
      const polygon = rectToPolygon(area, 2);
      let borderPoints;
      
      if (this.isSphere) {
        borderPoints = polygon.points.map((pt) => latLngToXYZ(pt.lat, pt.lng, 1.002));
      } else {
        borderPoints = polygon.points.map((pt) => {
          // 射线交点
          const surfacePoint = latLngToModelSurface(pt.lat, pt.lng, this.earthMesh);
          return surfacePoint;
        });
      }
      
      // 保证线闭合
      if (borderPoints.length > 0 && !borderPoints[0].equals(borderPoints[borderPoints.length - 1])) {
        borderPoints.push(borderPoints[0].clone());
      }
      
      // 巡视区使用橙色
      const color = 0xff6b35;
      const line = createLine(borderPoints, color);
      this.earthMesh.add(line);
      this.patrolAreaLines.push(line);
    });

    // 为巡视区创建文本标签
    this.patrolAreas.forEach((area) => {
      if (area.name) {
        // 计算矩形中心点
        const centerLat = (area.minLat + area.maxLat) / 2;
        const centerLng = (area.minLng + area.maxLng) / 2;

        this.createPatrolAreaLabel(area.name, centerLat, centerLng, area);
      }
    });
  }

  private createPatrolAreaLabel(text: string, lat: number, lng: number, area: LatLngRect) {
    // 创建固定屏幕大小的文字标签，跟随地球旋转
    const texture = this.createTextTexture(text, {
      hasShadow: true,
      hasStroke: false,
      textColor: "#ff6b35", // 巡视区使用橙色
    });

    if (!texture) return;

    const material = new THREE.SpriteMaterial({
      map: texture,
      transparent: true,
      depthTest: false, // 禁用深度测试，文字不会被地球遮挡
      depthWrite: false, // 不写入深度缓冲
    });

    const sprite = new THREE.Sprite(material);

    // 存储巡视区信息
    sprite.userData = { area, isPatrolArea: true };

    // 初始缩放，会在animate中动态调整
    sprite.scale.set(1, 1, 1);

    // 设置位置
    const position = latLngToXYZ(lat, lng, 1.01);
    sprite.position.copy(position);

    // 添加到场景（不是earthMesh的子级，通过applyEuler跟随旋转）
    this.scene.add(sprite);
    this.patrolAreaLabels.push(sprite);
  }

  private renderLocationPoints() {
    // 清除现有的地点标签
    this.locationLabels.forEach((label) => {
      this.scene.remove(label);
      if (label.material) {
        if (label.material.map) label.material.map.dispose();
        label.material.dispose();
      }
    });

    // 重新构建locationLabels数组
    this.locationLabels = [];

    // 获取当前地图级别
    const currentLevel = this.getZoomLevel();

    // 根据地图级别过滤location，只显示level小于等于地图level的location
    const filteredLocations = this.locationPoints.filter((location) => {
      return location.level <= currentLevel;
    });

    // 创建新的地点标签
    filteredLocations.forEach((location) => {
      this.createLocationMarker(location);
    });
  }

  private createLocationMarker(location: LocationPoint) {
    // 只创建文字标签，文字标签本身就可以被点击
    this.createLocationLabel(location);
  }

  private createLocationLabel(location: LocationPoint) {
    // 创建固定屏幕大小的文字标签，跟随地球旋转
    const texture = this.createTextTexture(location.name, {
      hasShadow: true,
      hasStroke: false,
      backgroundColor: "#ff0000", // 添加红色背景
    });

    if (!texture) return;

    const material = new THREE.SpriteMaterial({
      map: texture,
      transparent: true,
      depthTest: false, // 禁用深度测试，文字不会被地球遮挡
      depthWrite: false, // 不写入深度缓冲
    });

    const sprite = new THREE.Sprite(material);
    sprite.name = 123;

    // 存储地点信息
    sprite.userData = { location };

    // 初始缩放，会在animate中动态调整
    sprite.scale.set(1, 1, 1);

    // 添加到场景（不是earthMesh的子级，通过applyEuler跟随旋转）
    this.scene.add(sprite);
    this.locationLabels.push(sprite);
  }

  private renderDetectionPoints() {
    // 清除现有的探测点标签和连线
    this.detectionPointLabels.forEach((label) => {
      this.scene.remove(label);
      if (label.material) {
        if (label.material.map) label.material.map.dispose();
        label.material.dispose();
      }
    });

    // 清除探测点连线
    this.detectionPointLines.forEach((line) => {
      this.scene.remove(line);
      if (line.geometry) line.geometry.dispose();
      if (line.material) line.material.dispose();
    });

    // 重新构建数组
    this.detectionPointLabels = [];
    this.detectionPointLines = [];

    if (this.detectionPoints.length === 0) {
      return;
    }

    // 绘制探测点连线
    if (this.detectionPoints.length > 1) {
      this.renderDetectionPointLines();
    }

    // 创建探测点标记
    this.detectionPoints.forEach((point, index) => {
      this.createDetectionPointMarker(point, index);
    });
  }

  private renderDetectionPointLines() {
    if (this.detectionPoints.length < 2) {
      return;
    }

    // 为每对相邻的探测点创建大圆弧连线
    for (let i = 0; i < this.detectionPoints.length - 1; i++) {
      const point1 = this.detectionPoints[i];
      const point2 = this.detectionPoints[i + 1];

      // 创建球体表面的大圆弧，使用稍高的半径避免被遮挡
      const points = this.createGreatCircleArc(
        point1.center_latitude,
        point1.center_longitude,
        point2.center_latitude,
        point2.center_longitude,
        1.001 // 使用稍高的半径避免被地球遮挡
      );

      const geometry = new THREE.BufferGeometry().setFromPoints(points);
      const material = new THREE.LineBasicMaterial({
        color: 0xff6b35,
        linewidth: 5,
        depthTest: false, // 禁用深度测试，确保连线不被地球遮挡
      });

      const line = new THREE.Line(geometry, material);
      line.renderOrder = 1; // 设置渲染顺序，确保连线在地球之上渲染
      this.scene.add(line);
      this.detectionPointLines.push(line);
    }
  }

  private createGreatCircleArc(
    lat1: number,
    lng1: number,
    lat2: number,
    lng2: number,
    radius: number
  ): THREE.Vector3[] {
    const points: THREE.Vector3[] = [];

    // 计算两点之间的距离
    const lat1Rad = (lat1 * Math.PI) / 180;
    const lng1Rad = (lng1 * Math.PI) / 180;
    const lat2Rad = (lat2 * Math.PI) / 180;
    const lng2Rad = (lng2 * Math.PI) / 180;

    const dLat = lat2Rad - lat1Rad;
    const dLng = lng2Rad - lng1Rad;

    // 计算大圆距离
    const a =
      Math.sin(dLat / 2) * Math.sin(dLat / 2) +
      Math.cos(lat1Rad) * Math.cos(lat2Rad) * Math.sin(dLng / 2) * Math.sin(dLng / 2);
    const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    const distance = c;

    // 参考rectToPolygon的分段采样方式
    const step = Math.max(1, Math.min(5, distance * 10)); // 根据距离动态调整步长
    const segments = Math.max(10, Math.floor(distance * 20));

    for (let i = 0; i <= segments; i++) {
      const t = i / segments;

      // 使用球面插值计算中间点的经纬度
      const A = Math.sin((1 - t) * distance) / Math.sin(distance);
      const B = Math.sin(t * distance) / Math.sin(distance);

      const lat = Math.asin(A * Math.sin(lat1Rad) + B * Math.sin(lat2Rad));
      const lng = Math.atan2(
        A * Math.cos(lat1Rad) * Math.sin(lng1Rad) + B * Math.cos(lat2Rad) * Math.sin(lng2Rad),
        A * Math.cos(lat1Rad) * Math.cos(lng1Rad) + B * Math.cos(lat2Rad) * Math.cos(lng2Rad)
      );

      // 使用latLngToXYZ函数转换为3D坐标，与rectToPolygon保持一致
      const point = latLngToXYZ((lat * 180) / Math.PI, (lng * 180) / Math.PI, radius);
      points.push(point);
    }

    return points;
  }

  private createDetectionPointMarker(point: DetectionPoint, index: number) {
    // 创建探测点标记（使用Sprite保持屏幕投影大小不变）
    const markerTexture = this.createCircleTexture("#ff6b35", 32);

    let marker: THREE.Sprite | null = null;

    if (markerTexture) {
      const markerMaterial = new THREE.SpriteMaterial({
        map: markerTexture,
        transparent: true,
        depthTest: false,
        depthWrite: false,
      });

      marker = new THREE.Sprite(markerMaterial);

      // 设置位置
      if (this.isSphere) {
        const position = latLngToXYZ(point.center_latitude, point.center_longitude, 1.002);
        marker.position.copy(position);
      } else {
        const box = new THREE.Box3().setFromObject(this.earthMesh);
        const center = new THREE.Vector3();
        box.getCenter(center);
        const size = new THREE.Vector3();
        box.getSize(size);
        const radius = Math.max(size.x, size.y, size.z) / 2;
        const position = latLngToXYZ(point.center_latitude, point.center_longitude, radius).add(center);
        marker.position.copy(position);
      }

      // 存储探测点信息
      marker.userData = { detectionPoint: point, isCircleMarker: true };

      this.scene.add(marker);
      this.detectionPointLabels.push(marker);
    }

    // 创建序号标签
    const numberTexture = this.createTextTexture((index + 1).toString(), {
      hasShadow: true,
      hasStroke: true,
      strokeColor: "#ffffff",
      strokeWidth: 2,
    });

    if (numberTexture && marker) {
      const numberMaterial = new THREE.SpriteMaterial({
        map: numberTexture,
        transparent: true,
        depthTest: false,
        depthWrite: false,
      });

      const numberSprite = new THREE.Sprite(numberMaterial);
      numberSprite.position.copy(marker.position);
      numberSprite.position.z += 0.01; // 稍微偏移，避免重叠
      numberSprite.userData = { detectionPoint: point, isNumberLabel: true };

      this.scene.add(numberSprite);
      this.detectionPointLabels.push(numberSprite);
    }

    // 创建探测点名称标签
    const labelTexture = this.createTextTexture(point.name, {
      hasShadow: true,
      hasStroke: false,
    });

    if (labelTexture && marker) {
      const labelMaterial = new THREE.SpriteMaterial({
        map: labelTexture,
        transparent: true,
        depthTest: false,
        depthWrite: false,
      });

      const labelSprite = new THREE.Sprite(labelMaterial);
      labelSprite.position.copy(marker.position);
      labelSprite.position.z += 0.05; // 增加偏移，让label更靠上
      labelSprite.userData = { detectionPoint: point, isNameLabel: true, isDetectionPointLabel: true };

      this.scene.add(labelSprite);
      this.detectionPointLabels.push(labelSprite);
    }
  }

  private renderPolygons() {
    if (!this.earthMesh) {
      return;
    }
    clearLines(this.polygonLines, this.earthMesh);
    this.polygonLines = [];

    // 清理旧的文本标签
    this.textMeshes.forEach((mesh) => {
      this.scene.remove(mesh);
      if (mesh.geometry) mesh.geometry.dispose();
      if (mesh.material) {
        if (mesh.material.map) mesh.material.map.dispose();
        mesh.material.dispose();
      }
    });
    this.textMeshes = [];

    // 先将 rects 转为 polygon
    let allPolygons: LatLngPolygon[] = [];
    if (this.rects && this.rects.length > 0) {
      allPolygons = this.rects.map((r) => rectToPolygon(r, 2));
    }
    if (this.polygons && this.polygons.length > 0) {
      allPolygons = allPolygons.concat(this.polygons);
    }

    // 绘制多边形线框
    allPolygons.forEach((polygon, idx) => {
      let borderPoints;
      if (this.isSphere) {
        borderPoints = polygon.points.map((pt) => latLngToXYZ(pt.lat, pt.lng, 1.002));
      } else {
        borderPoints = polygon.points.map((pt) => {
          // 计算包围盒center和radius
          const box = new THREE.Box3().setFromObject(this.earthMesh);
          const center = new THREE.Vector3();
          box.getCenter(center);
          const size = new THREE.Vector3();
          box.getSize(size);
          const radius = Math.max(size.x, size.y, size.z) / 2;

          // 理论球面点（用包围盒center和radius）
          const theoryPoint = latLngToXYZ(pt.lat, pt.lng, radius).add(center);
          const theoryMarker = createMarker(theoryPoint, this.camera, this.renderer, 0x0000ff, 8); // 蓝色
          this.earthMesh.add(theoryMarker);

          // 射线交点
          const surfacePoint = latLngToModelSurface(pt.lat, pt.lng, this.earthMesh);
          const surfaceMarker = createMarker(surfacePoint, this.camera, this.renderer, 0xff00ff, 10); // 紫色
          this.earthMesh.add(surfaceMarker);

          // 画线依然用surfacePoint
          return surfacePoint;
        });
      }
      // 保证线闭合
      if (borderPoints.length > 0 && !borderPoints[0].equals(borderPoints[borderPoints.length - 1])) {
        borderPoints.push(borderPoints[0].clone());
      }
      const color = this.intersectedPolygons[idx] ? 0xff0000 : 0x00ff00;
      const line = createLine(borderPoints, color);
      this.earthMesh.add(line);
      this.polygonLines.push(line);
    });

    // 为矩形区域创建文本标签
    this.rects.forEach((rect, idx) => {
      if (rect.name) {
        // 计算矩形中心点
        const centerLat = (rect.minLat + rect.maxLat) / 2;
        const centerLng = (rect.minLng + rect.maxLng) / 2;

        const textMesh = this.createSurfaceText(rect.name, centerLat, centerLng);
        if (textMesh) {
          this.scene.add(textMesh);
          this.textMeshes.push(textMesh);
        }
      }
    });
  }

  private getLocationAtPoint(event: MouseEvent): LocationPoint | null {
    if (!this.camera || !this.renderer || !this.earthMesh) {
      return null;
    }

    // 获取鼠标射线
    const mouse = new THREE.Vector2();
    const rect = this.renderer.domElement.getBoundingClientRect();
    mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
    mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;

    const raycaster = new THREE.Raycaster();
    raycaster.setFromCamera(mouse, this.camera);

    // 只检测可见的标签
    const visibleLabels = this.locationLabels.filter(label => label.visible);
    
    // 检测与可见文字标签的相交
    const labelIntersects = raycaster.intersectObjects(visibleLabels);
    if (labelIntersects.length > 0) {
      const clickedObject = labelIntersects[0].object;
      if (clickedObject.userData && clickedObject.userData.location) {
        return clickedObject.userData.location;
      }
    }

    return null;
  }

  private getDetectionPointAtPoint(event: MouseEvent): DetectionPoint | null {
    if (!this.camera || !this.renderer || !this.earthMesh) {
      return null;
    }

    // 获取鼠标射线
    const mouse = new THREE.Vector2();
    const rect = this.renderer.domElement.getBoundingClientRect();
    mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
    mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;

    const raycaster = new THREE.Raycaster();
    raycaster.setFromCamera(mouse, this.camera);

    // 检测与探测点标签的相交
    const labelIntersects = raycaster.intersectObjects(this.detectionPointLabels);
    if (labelIntersects.length > 0) {
      const clickedObject = labelIntersects[0].object;
      if (clickedObject.userData && clickedObject.userData.detectionPoint) {
        return clickedObject.userData.detectionPoint;
      }
    }

    return null;
  }

  private getPatrolAreaAtPoint(event: MouseEvent): LatLngRect | null {
    if (!this.camera || !this.renderer || !this.earthMesh) {
      return null;
    }

    // 获取鼠标射线
    const mouse = new THREE.Vector2();
    const rect = this.renderer.domElement.getBoundingClientRect();
    mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
    mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;

    const raycaster = new THREE.Raycaster();
    raycaster.setFromCamera(mouse, this.camera);

    // 检测与巡视区标签的相交
    const labelIntersects = raycaster.intersectObjects(this.patrolAreaLabels);
    if (labelIntersects.length > 0) {
      const clickedObject = labelIntersects[0].object;
      if (clickedObject.userData && clickedObject.userData.area) {
        return clickedObject.userData.area;
      }
    }

    return null;
  }

  private getMouseLatLng(event: MouseEvent): LatLngPoint | null {
    if (!this.camera || !this.renderer || !this.earthMesh) {
      return null;
    }
    // 根据模型类型选择不同的拾取方式
    let hit;
    if (this.isSphere) {
      hit = getMouseRaycast(event, this.camera, this.renderer, this.earthMesh as THREE.Mesh);
    } else {
      hit = getMouseRaycastForModel(event, this.camera, this.renderer, this.earthMesh);
    }
    if (!hit) return null;
    return isNaN(hit.lat) || isNaN(hit.lng) ? null : { lat: hit.lat, lng: hit.lng };
  }

  private renderSelection() {
    if (!this.earthMesh) {
      return;
    }
    // 清理旧线框
    if (this.selectionLine) {
      this.earthMesh.remove(this.selectionLine);
      this.selectionLine = null;
    }
    if (this.selectionPoints && this.selectionPoints.length === 2) {
      // 画矩形线框
      const [p1, p2] = this.selectionPoints;

      // 使用新的函数处理经度范围，考虑跨越-180到180度的情况
      const lngRange = getCorrectLngRange(p1.lng, p2.lng);

      const rect = {
        minLat: Math.min(p1.lat, p2.lat),
        maxLat: Math.max(p1.lat, p2.lat),
        minLng: lngRange.minLng,
        maxLng: lngRange.maxLng,
      };
      const polygon = rectToPolygon(rect, 2);
      const borderPoints = polygon.points.map((pt) => latLngToXYZ(pt.lat, pt.lng, 1.0013));
      const line = createLine(borderPoints, 0xffff00);
      this.earthMesh.add(line);
      this.selectionLine = line;
    }
  }

  public setSelection(points: [LatLngPoint, LatLngPoint] | null) {
    this.selectionPoints = points;
    this.selectionActive = false;
    this.renderSelection();
    this.emitSelectionChange();
  }

  public setExternalSelection(selectionRect: LatLngRect | null) {
    // 如果正在选区中，不更新内部状态，避免循环更新
    if (this.selectionActive && this.selectionClickCount === 1) {
      return;
    }

    if (selectionRect) {
      // 将LatLngRect转换为两个点
      const point1: LatLngPoint = { lat: selectionRect.minLat, lng: selectionRect.minLng };
      const point2: LatLngPoint = { lat: selectionRect.maxLat, lng: selectionRect.maxLng };
      this.selectionPoints = [point1, point2];
      this.selectionActive = false;
      this.updateIntersectedPolygons();
      this.renderSelection();
      this.renderPolygons();
    } else {
      this.selectionPoints = null;
      this.intersectedPolygons = [];
      this.renderSelection();
      this.renderPolygons();
    }
  }

  private emitSelectionChange() {
    // This method is no longer used as onSelectionChange is removed from constructor
    // Keeping it for now as it might be re-introduced or removed later.
  }

  // 删除setMarker方法
  // 删除renderMarker方法

  public setSelectionActive(active: boolean) {
    this.externalSelectionActive = active;
    this.selectionClickCount = 0;
    this.selectionActive = false;
    this.updateCursorStyle();
  }

  private updateIntersectedPolygons() {
    if (!this.selectionPoints || this.selectionPoints.length !== 2) {
      this.intersectedPolygons = [];
      this.intersectedDetectionPoints = [];
      return;
    }
    const [p1, p2] = this.selectionPoints;
    const lngRange = getCorrectLngRange(p1.lng, p2.lng);
    const selectionRect = {
      minLat: Math.min(p1.lat, p2.lat),
      maxLat: Math.max(p1.lat, p2.lat),
      minLng: lngRange.minLng,
      maxLng: lngRange.maxLng,
    };

    let allPolygons: LatLngPolygon[] = [];
    let intersectedResults: boolean[] = [];

    // 处理矩形区域
    if (this.rects && this.rects.length > 0) {
      const rectPolygons = this.rects.map((r) => rectToPolygon(r, 2));
      allPolygons = allPolygons.concat(rectPolygons);

      // 使用rectIntersects进行矩形相交判断
      const rectResults = this.rects.map((rect) => rectIntersects(rect, selectionRect));
      intersectedResults = intersectedResults.concat(rectResults);
    }

    // 处理多边形区域
    if (this.polygons && this.polygons.length > 0) {
      allPolygons = allPolygons.concat(this.polygons);

      // 使用polygonIntersectsRect进行多边形相交判断
      const polygonResults = this.polygons.map((polygon) => polygonIntersectsRect(polygon, selectionRect));
      intersectedResults = intersectedResults.concat(polygonResults);
    }

    this.intersectedPolygons = intersectedResults;

    // 处理探测点相交
    if (this.detectionPoints && this.detectionPoints.length > 0) {
      const detectionPointResults = this.detectionPoints.map((point) =>
        pointIntersectsRect({ lat: point.center_latitude, lng: point.center_longitude }, selectionRect)
      );
      this.intersectedDetectionPoints = detectionPointResults;

      // 更新探测点高亮
      this.updateDetectionPointHighlights();
    } else {
      this.intersectedDetectionPoints = [];
    }
  }

  private getIntersectedPolygons(): LatLngPolygon[] {
    let allPolygons: LatLngPolygon[] = [];
    if (this.rects && this.rects.length > 0) {
      allPolygons = this.rects.map((r) => rectToPolygon(r, 2));
    }
    if (this.polygons && this.polygons.length > 0) {
      allPolygons = allPolygons.concat(this.polygons);
    }

    return allPolygons.filter((_, idx) => this.intersectedPolygons[idx]);
  }

  // 更新探测点高亮
  private updateDetectionPointHighlights() {
    console.log("updateDetectionPointHighlights被调用");
    console.log("detectionPointLabels长度:", this.detectionPointLabels.length);
    console.log("intersectedDetectionPoints:", this.intersectedDetectionPoints);

    this.detectionPointLabels.forEach((label, index) => {
      if (label.userData.isCircleMarker) {
        const isIntersected = this.intersectedDetectionPoints[index];
        console.log(`探测点${index}: 相交=${isIntersected}`);
        const sprite = label as THREE.Sprite;
        const material = sprite.material as THREE.SpriteMaterial;

        if (isIntersected) {
          // 高亮颜色：红色
          const highlightTexture = this.createCircleTexture("#ff0000", 32);
          if (highlightTexture) {
            material.map = highlightTexture;
            material.needsUpdate = true;
            console.log(`探测点${index}: 设置为高亮`);
          }
        } else {
          // 正常颜色：橙色
          const normalTexture = this.createCircleTexture("#ff6b35", 32);
          if (normalTexture) {
            material.map = normalTexture;
            material.needsUpdate = true;
            console.log(`探测点${index}: 设置为正常`);
          }
        }
      }
    });
  }

  public getCameraZ(): number {
    return this.camera?.position.z || 3;
  }

  public setCameraZ(z: number) {
    if (this.camera) {
      this.camera.position.z = Math.max(1.05, Math.min(30, z));
    }
  }

  public zoomIn() {
    if (this.camera) {
      const currentZ = this.camera.position.z;
      const newZ = Math.max(1.05, currentZ / 1.05);
      this.camera.position.z = newZ;
    }
  }

  public zoomOut() {
    if (this.camera) {
      const currentZ = this.camera.position.z;
      const newZ = Math.min(5, currentZ * 1.05);
      this.camera.position.z = newZ;
    }
  }

  public resetView() {
    if (this.camera && this.earthMesh) {
      // 计算合适的相机Z位置，让地球直径比容器宽高小20px
      const width = this.container.clientWidth;
      const height = this.container.clientHeight;
      const earthDiameter = 2; // 地球直径（单位球）
      const targetScreenDiameter = Math.min(width, height); // 目标屏幕直径
      // 使用简单的比例关系：cameraZ与屏幕大小成反比
      const cameraZ = (earthDiameter * height) / targetScreenDiameter;

      // 重置相机位置到计算出的合适位置
      this.camera.position.z = cameraZ;
      // 重置地球旋转到默认状态，让地球朝向经度0度（格林威治子午线）
      this.earthMesh.rotation.x = 0;
      this.earthMesh.rotation.y = -Math.PI / 2; // -90度，让地球朝向经度0度
      // 清除惯性
      this.inertia = { vx: 0, vy: 0 };
    }
  }

  public getScaleInfo(): { px: number; km: number } {
    if (!this.camera || !this.renderer) {
      return { px: 100, km: 1000 };
    }
    const px = 100;

    // 优先使用基于射线拾取的局部像素比例：以屏幕中心为基准点，水平偏移dx像素
    try {
      if (this.earthMesh && this.camera && this.renderer) {
        const rect = this.renderer.domElement.getBoundingClientRect();
        const centerX = rect.left + rect.width / 2;
        const centerY = rect.top + rect.height / 2;
        const dx = 1; // 1像素

        const ndcFromClient = (clientX: number, clientY: number) => {
          return new THREE.Vector2(
            ((clientX - rect.left) / rect.width) * 2 - 1,
            -((clientY - rect.top) / rect.height) * 2 + 1
          );
        };

        const raycaster = new THREE.Raycaster();
        const intersectAt = (clientX: number, clientY: number): THREE.Vector3 | null => {
          const ndc = ndcFromClient(clientX, clientY);
          raycaster.setFromCamera(ndc, this.camera);
          const hits = raycaster.intersectObject(this.earthMesh, false);
          if (!hits || hits.length === 0) return null;
          return hits[0].point.clone(); // world space
        };

        const p1 = intersectAt(centerX, centerY);
        let p2 = intersectAt(centerX + dx, centerY);
        if (!p2) p2 = intersectAt(centerX - dx, centerY);

        if (p1 && p2) {
          // 转到地球本地坐标，归一化后求夹角，数值稳定
          const v1 = this.earthMesh.worldToLocal(p1.clone()).normalize();
          const v2 = this.earthMesh.worldToLocal(p2.clone()).normalize();
          let cosTheta = v1.dot(v2);
          cosTheta = Math.min(1, Math.max(-1, cosTheta));
          const theta = Math.acos(cosTheta); // 弧度

          const arcKmForDx = this.radius * theta; // 弧长 = R * θ
          const perPixelKm = arcKmForDx / Math.abs(dx);
          const distance100pxKm = perPixelKm * px;
          const km = Math.max(1, Math.round(distance100pxKm));
          return { px, km };
        }
      }
    } catch (_) {
      // 忽略并使用兜底算法
    }

    // 兜底：使用可见弧度/屏幕直径估算
    const earthPx = getEarthScreenDiameterPrecise(this.camera, this.renderer);
    const visibleArcRadians = getVisibleArcBySampling(this.camera, this.renderer);
    const kmPerPx = (this.radius * visibleArcRadians) / Math.max(earthPx, 1);
    const km = Math.max(1, Math.round(kmPerPx * px));
    return { px, km };
  }

  // 根据zoom动态加载tile
  private updateTiles() {
    this.sceneVersion++;
    const currentVersion = this.sceneVersion;
    const level = this.getZoomLevel();
    // 记录当前所有旧tile mesh
    const oldTileMeshes = new Map(this.tileMeshes);
    // level=1时只显示1_0_0.png，隐藏基础贴图和其他tile
    if (level === 1) {
      // 立即清理所有普通tile
      for (const [key, mesh] of this.tileMeshes) {
        this.earthMesh.remove(mesh);
        this.disposeMesh(mesh);
      }
      this.tileMeshes.clear();
      // 隐藏基础贴图
      if (this.earthMesh.material) {
        if (Array.isArray(this.earthMesh.material)) {
          this.earthMesh.material.forEach((m) => {
            if (m.map) {
              m.opacity = 0;
              m.transparent = true;
            }
          });
        } else {
          if (this.earthMesh.material.map) {
            this.earthMesh.material.opacity = 0;
            this.earthMesh.material.transparent = true;
          }
        }
      }
      // 确保level1的mesh显示
      if (this.level1TileMesh && !this.earthMesh.children.includes(this.level1TileMesh)) {
        this.earthMesh.add(this.level1TileMesh);
      }
      // 注意：level1的1_0_0大图mesh永远不清除，始终作为底图存在
      return;
    } else {
      // 非level=1时，1_0_0大图mesh不清除，始终作为底图存在
      // 统计需要加载的新tile数量
      let loadedCount = 0;
      const visibleTiles = this.getVisibleTiles(level);
      const totalToLoad = visibleTiles.filter((tile) => !this.tileMeshes.has(tile.key)).length;
      if (totalToLoad === 0) {
        // 没有新tile需要加载，立即清理旧tile
        for (const [key, mesh] of oldTileMeshes) {
          if (!visibleTiles.some((t) => t.key === key)) {
            this.earthMesh.remove(mesh);
            this.disposeMesh(mesh);
            this.tileMeshes.delete(key);
          }
        }
        return;
      }
      // 加载新tile
      for (const tile of visibleTiles) {
        if (!this.tileMeshes.has(tile.key)) {
          this.loadTile(tile.z, tile.x, tile.y, currentVersion).then((mesh) => {
            if (mesh) {
              this.earthMesh.add(mesh);
              this.tileMeshes.set(tile.key, mesh);
            }
            loadedCount++;
            if (loadedCount === totalToLoad) {
              // 新tile全部加载好后再清理旧tile
              for (const [key, mesh] of oldTileMeshes) {
                if (!visibleTiles.some((t) => t.key === key)) {
                  this.earthMesh.remove(mesh);
                  this.disposeMesh(mesh);
                  this.tileMeshes.delete(key);
                }
              }
            }
          });
        }
      }
      // 不再立即移除不可见tile，等新tile加载好后统一清理
      // 隐藏基础贴图
      if (this.earthMesh.material) {
        if (Array.isArray(this.earthMesh.material)) {
          this.earthMesh.material.forEach((m) => {
            if (m.map) {
              m.opacity = 0;
              m.transparent = true;
            }
          });
        } else {
          if (this.earthMesh.material.map) {
            this.earthMesh.material.opacity = 0;
            this.earthMesh.material.transparent = true;
          }
        }
      }
    }
  }
  // 根据可视区域弧度和地球屏幕直径计算圆周周长，确定合适的level
  private getZoomLevel(): number {
    if (!this.camera || !this.renderer) {
      return this.minZoom;
    }

    const width = this.renderer.domElement.clientWidth;
    const height = this.renderer.domElement.clientHeight;
    if (width === 0 || height === 0) {
      return this.minZoom;
    }

    // 获取地球屏幕直径（像素）
    const earthPx = getEarthScreenDiameterPrecise(this.camera, this.renderer);

    // 获取可视区域弧度
    const visibleArc = getVisibleArcBySampling(this.camera, this.renderer);

    // 计算可视区域对应的圆周长度（像素）
    // 可视区域圆周长度 = 圆周周长 * (可视区域弧度 / 2π)
    const visibleCircumferencePx = earthPx / (visibleArc / (2 * Math.PI));

    // 根据可视区域圆周长度确定level
    // 每个level的瓦片分辨率不同，需要根据实际瓦片尺寸调整阈值
    const levelThresholds = [
      1024, // level1: 1024px对应整个地球
      2048, // level2: 2048px对应整个地球
      4096, // level3: 4096px对应整个地球
      8192, // level4: 8192px对应整个地球
      16384, // level5: 16384px对应整个地球
	  32768, // level6: 32768px对应整个地球
	  65536, // level7: 65536px对应整个地球
	  131072, // level8: 131072px对应整个地球
	  262144, // level9: 262144px对应整个地球
	  524288, // level10: 524288px对应整个地球
    ];

    // 从高level开始检查，找到第一个满足条件的level
    for (let i = levelThresholds.length - 1; i >= 0; i--) {
      if (visibleCircumferencePx >= levelThresholds[i] / 2) {
        const level = i + 1;
        // 确保level在有效范围内
        return Math.max(this.minZoom, Math.min(this.maxZoom, level));
      }
    }

    return this.minZoom;
  }
  // 理论最优：任意采样点与相机夹角小于等于arccos(1/z)则加载
  public getVisibleTiles(level: number, debug = false): { z: number; x: number; y: number; key: string }[] {
    this.earthMesh.updateMatrixWorld(true);
    const tiles: { z: number; x: number; y: number; key: string }[] = [];
    const rows = Math.pow(2, level - 1);
    const cols = Math.pow(2, level);
    const cameraPos = this.camera.position.clone();
    const vCamera = cameraPos.clone().normalize();
    const z = cameraPos.length();
    const maxAngle = Math.acos(1 / z); // 弧度
    if (debug) {
      console.log(1, (maxAngle / Math.PI) * 180);
      // 计算摄像机正中央视线与球体表面的交点经纬度
      const cameraPos = this.camera.getWorldPosition(new THREE.Vector3());
      const viewDir = new THREE.Vector3();
      this.camera.getWorldDirection(viewDir);
      const sphereCenter = this.earthMesh.getWorldPosition(new THREE.Vector3());
      const sphereRadius = 1; // 如果有缩放，这里要乘以缩放系数
      const oc = cameraPos.clone().sub(sphereCenter);
      const a = viewDir.dot(viewDir);
      const b = 2 * oc.dot(viewDir);
      const c = oc.dot(oc) - sphereRadius * sphereRadius;
      const discriminant = b * b - 4 * a * c;
      if (discriminant >= 0) {
        const t = (-b - Math.sqrt(discriminant)) / (2 * a);
        const hit = cameraPos.clone().add(viewDir.clone().multiplyScalar(t));
        const earthMatrixWorldInv = new THREE.Matrix4().copy(this.earthMesh.matrixWorld).invert();
        const localHit = hit.clone().applyMatrix4(earthMatrixWorldInv);
        const r = localHit.length();
        const lat = (Math.asin(localHit.y / r) * 180) / Math.PI;
        const lon = (-Math.atan2(localHit.z, localHit.x) * 180) / Math.PI;
        console.log("摄像机正中央的球体表面经纬度:", { lat, lon });
      } else {
        console.log("摄像机视线未与球体相交");
      }
    }
    const earthMatrix = this.earthMesh.matrixWorld;
    for (let y = 0; y < rows; y++) {
      for (let x = 0; x < cols; x++) {
        // 采样patch的中心、四角、边中点
        const lat1 = 90 - (y / rows) * 180;
        const lat2 = 90 - ((y + 1) / rows) * 180;
        const lon1 = -180 + (x / cols) * 360;
        const lon2 = -180 + ((x + 1) / cols) * 360;
        const latMid = (lat1 + lat2) / 2;
        const lonMid = (lon1 + lon2) / 2;
        const r = this.getTileRadius(level);
        const samplePoints = [
          [lat1, lon1], // 左上
          [lat1, lon2], // 右上
          [lat2, lon1], // 左下
          [lat2, lon2], // 右下
          [latMid, lonMid], // 中心
          [lat1, lonMid], // 上中
          [lat2, lonMid], // 下中
          [latMid, lon1], // 左中
          [latMid, lon2], // 右中
        ];
        let visible = false;
        for (const [lat, lon] of samplePoints) {
          const phi = THREE.MathUtils.degToRad(90 - lat);
          const theta = -THREE.MathUtils.degToRad(lon); // 加负号，确保与贴图一致
          const localPos = new THREE.Vector3(
            r * Math.sin(phi) * Math.cos(theta),
            r * Math.cos(phi),
            r * Math.sin(phi) * Math.sin(theta)
          );
          const worldPos = localPos.clone().applyMatrix4(earthMatrix);
          const vPatch = worldPos.clone().normalize();
          const angle = vPatch.angleTo(vCamera);
          if (debug) {
            console.log(2, (angle / Math.PI) * 180, lat, lon, x, y, level, angle <= maxAngle);
          }
          if (angle <= maxAngle) {
            visible = true;
            break;
          }
        }
        if (visible) {
          tiles.push({ z: level, x, y, key: `${level}_${x}_${y}` });
        }
      }
    }
    this.visibleTileKeys = tiles.map((t) => t.key);
    return tiles;
  }
  // 获取不同level的patch半径
  private getTileRadius(level: number): number {
    return 1 + 0.001;
  }
  // 加载单个tile并贴到球体表面，patch半径用getTileRadius
  private async loadTile(z: number, x: number, y: number, version?: number, retry = 0): Promise<THREE.Mesh | null> {
    if (!this.tileUrlTemplate) {
      return null;
    }
    
    const textureKey = `${z}_${x}_${y}`;
    
    // 检查是否已经失败过，避免重复尝试
    if (this.failedTileCache.has(textureKey)) {
      return null;
    }
    
    let url = this.tileUrlTemplate;
    if (url.includes("{quadkey}")) {
      const quadkey = tileXYToQuadKey(x, y, z);
      url = url.replace("{quadkey}", quadkey);
    } else {
      url = url
        .replace("{z}", z.toString())
        .replace("{x}", (x + 1).toString())
        .replace("{y}", (y + 1).toString());
    }
    
    let promise = this.tileTexturePromiseCache.get(textureKey);
    if (!promise) {
      promise = new Promise((resolve, reject) => {
        new THREE.TextureLoader().load(
          url,
          (texture) => {
            texture.colorSpace = THREE.SRGBColorSpace;
            this.textureCache.set(textureKey, texture);
            this.tileTexturePromiseCache.delete(textureKey);
            resolve(texture);
          },
          undefined,
          (error) => {
            this.tileTexturePromiseCache.delete(textureKey);
            reject(error);
          }
        );
      });
      this.tileTexturePromiseCache.set(textureKey, promise);
    }
    let texture: THREE.Texture;
    try {
      texture = await promise;
    } catch (e) {
      if (retry < 1) {
        return this.loadTile(z, x, y, version, retry + 1);
      }
      // 重试失败后，将瓦片标记为失败，避免后续重复尝试
      this.failedTileCache.add(textureKey);
      console.warn(`瓦片加载失败，已加入失败缓存: ${textureKey}`, e);
      return null;
    }
    // 场景版本校验，过时则丢弃
    if (typeof version === "number" && this.sceneVersion !== version) {
      return null;
    }
    // 计算tile对应经纬度范围（等经纬度分割）
    const rows = Math.pow(2, z - 1);
    const cols = Math.pow(2, z);
    const lon1 = (x / cols) * 360 - 180;
    const lon2 = ((x + 1) / cols) * 360 - 180;
    const lat1 = 90 - (y / rows) * 180;
    const lat2 = 90 - ((y + 1) / rows) * 180;
    const r = this.getTileRadius(z);
    // 创建球面patch
    const geometry = this.createTileGeometry(lat1, lat2, lon1, lon2, r);
    const material = new THREE.MeshBasicMaterial({ map: texture });
    return new THREE.Mesh(geometry, material);
  }
  // 创建tile球面patch，支持自定义半径
  private createTileGeometry(
    lat1: number,
    lat2: number,
    lon1: number,
    lon2: number,
    radius = 1.001
  ): THREE.BufferGeometry {
    // 提高细分度，保证贴图边缘平滑
    const widthSegments = 256;
    const heightSegments = 256;
    const geometry = new THREE.SphereGeometry(
      radius,
      widthSegments,
      heightSegments,
      Math.PI + THREE.MathUtils.degToRad(lon1),
      THREE.MathUtils.degToRad(lon2 - lon1),
      THREE.MathUtils.degToRad(90 - lat1),
      THREE.MathUtils.degToRad(lat1 - lat2)
    );
    return geometry;
  }
  // 新增方法：获取当前可见tile key
  public getVisibleTileKeys(): string[] {
    return this.visibleTileKeys;
  }

  // 获取内存使用信息
  public getMemoryInfo() {
    return {
      tileCount: this.tileMeshes.size,
      textureCount: this.textureCache.size,
      visibleTiles: this.visibleTileKeys.length,
    };
  }

  // 创建文本纹理
  private createCircleTexture(color: string, size: number = 64) {
    const canvas = document.createElement("canvas");
    canvas.width = size;
    canvas.height = size;
    const ctx = canvas.getContext("2d");

    if (!ctx) {
      return null;
    }

    // 清除画布
    ctx.clearRect(0, 0, size, size);

    // 绘制外圈（白色背景）
    ctx.beginPath();
    ctx.arc(size / 2, size / 2, size / 2 - 2, 0, 2 * Math.PI);
    ctx.fillStyle = "rgba(255, 255, 255, 0.9)";
    ctx.fill();

    // 绘制内圈（主色）
    ctx.beginPath();
    ctx.arc(size / 2, size / 2, size / 2 - 4, 0, 2 * Math.PI);
    ctx.fillStyle = color;
    ctx.fill();

    // 绘制中心点（白色）
    ctx.beginPath();
    ctx.arc(size / 2, size / 2, size / 2 - 6, 0, 2 * Math.PI);
    ctx.fillStyle = "#ffffff";
    ctx.fill();

    const texture = new THREE.CanvasTexture(canvas);
    texture.needsUpdate = true;
    return texture;
  }

  private createTextTexture(
    text: string,
    options: {
      hasShadow?: boolean;
      hasStroke?: boolean;
      strokeColor?: string;
      strokeWidth?: number;
      textColor?: string;
      backgroundColor?: string;
    } = {}
  ) {
    const canvas = document.createElement("canvas");
    const context = canvas.getContext("2d");
    if (!context) return null;

    // 设置文本样式来测量文字尺寸
    context.font = "bold 28px Arial";
    context.textAlign = "center";
    context.textBaseline = "middle";

    // 测量文字的实际尺寸
    const textMetrics = context.measureText(text);
    const textWidth = textMetrics.width;
    const textHeight = 28; // 字体大小

    canvas.width = textWidth; // 最小宽度100px
    canvas.height = textHeight + 6;

    // 绘制背景色（可选）
    if (options.backgroundColor) {
    //   context.fillStyle = options.backgroundColor;
    //   context.fillRect(0, 0, canvas.width, canvas.height);
    }

    // 重新设置文本样式（因为canvas尺寸改变了）
    context.font = "bold 28px Arial";
    context.fillStyle = options.textColor || "#ffffff";
    context.textAlign = "center";
    context.textBaseline = "middle";

    // 设置阴影（可选）
    if (options.hasShadow) {
      context.shadowColor = "rgba(0, 0, 0, 0.8)";
      context.shadowBlur = 4;
      context.shadowOffsetX = 2;
      context.shadowOffsetY = 2;
    }

    // 设置描边（可选）
    if (options.hasStroke) {
      context.strokeStyle = options.strokeColor || "#000000";
      context.lineWidth = options.strokeWidth || 3;
    }

    // 绘制文本
    const x = canvas.width / 2;
    const y = canvas.height / 2;

    if (options.hasStroke) {
      context.strokeText(text, x, y);
    }
    context.fillText(text, x, y);

    // 创建纹理
    const texture = new THREE.CanvasTexture(canvas);
    texture.needsUpdate = true;
    return texture;
  }

  // 在地球表面创建文本标签（Sprite幽灵贴图方式，可被地球遮挡）
  private createSurfaceText(text: string, lat: number, lng: number) {
    const texture = this.createTextTexture(text, {
      hasShadow: false,
      hasStroke: true,
    });
    if (!texture) {
      return null;
    }
    const material = new THREE.SpriteMaterial({
      map: texture,
      transparent: true,
      depthTest: false, // 禁用深度测试，文字不会被地球遮挡
      depthWrite: false, // 不写入深度缓冲
    });
    const sprite = new THREE.Sprite(material);
    // 初始scale，后续在animate中动态调整
    sprite.scale.set(1, 1, 1);
    const position = latLngToXYZ(lat, lng, 1.01);
    sprite.position.copy(position);
    return sprite;
  }

  // 经纬度转屏幕坐标
  public latLngToScreen(lat: number, lng: number) {
    if (!this.camera || !this.earthMesh) {
      return null;
    }

    // 转换为3D坐标
    const worldPoint = latLngToXYZ(lat, lng, 1.02);

    // 投影到屏幕
    const screenPoint = worldPoint.clone().project(this.camera);

    // 转换为像素坐标
    const width = this.renderer.domElement.clientWidth;
    const height = this.renderer.domElement.clientHeight;

    return {
      x: (screenPoint.x * 0.5 + 0.5) * width,
      y: (screenPoint.y * -0.5 + 0.5) * height,
      z: screenPoint.z,
    };
  }

  // 立即加载level1瓦片作为基础底图
  private loadLevel1Tile(retry = 0) {
    if (this.level1TileMesh) {
      return; // 已经加载过了
    }

    const url = this.tileUrlTemplate?.replace("{z}", "1").replace("{x}", "1").replace("{y}", "1") ?? "";
    const textureKey = "1_1_1";

    // 检查是否已经失败过，避免重复尝试
    if (this.failedTileCache.has(textureKey)) {
      console.warn("Level1瓦片已标记为失败，跳过加载");
      return;
    }

    // 检查是否已经有缓存的纹理
    let texture = this.textureCache.get(textureKey);
    if (texture) {
      this.createLevel1Mesh(texture);
      return;
    }

    // 检查是否正在加载
    let promise = this.tileTexturePromiseCache.get(textureKey);
    if (!promise) {
      promise = new Promise((resolve, reject) => {
        new THREE.TextureLoader().load(
          url,
          (texture) => {
            texture.colorSpace = THREE.SRGBColorSpace;
            this.textureCache.set(textureKey, texture);
            this.tileTexturePromiseCache.delete(textureKey);
            resolve(texture);
          },
          undefined,
          (error) => {
            this.tileTexturePromiseCache.delete(textureKey);
            reject(error);
          }
        );
      });
      this.tileTexturePromiseCache.set(textureKey, promise);
    }

    promise
      .then((texture) => {
        this.createLevel1Mesh(texture);
      })
      .catch((error) => {
        if (retry < 1) {
          console.warn(`Level1瓦片加载失败，正在重试 (${retry + 1}/1):`, error);
          // 延迟重试，避免立即重试
          setTimeout(() => {
            this.loadLevel1Tile(retry + 1);
          }, 1000);
        } else {
          // 重试失败后，将瓦片标记为失败，避免后续重复尝试
          this.failedTileCache.add(textureKey);
          console.error("Level1瓦片加载失败，已加入失败缓存:", error);
        }
      });
  }

  // 创建level1的mesh
  private createLevel1Mesh(texture: THREE.Texture) {
    if (this.level1TileMesh) {
      return; // 已经创建过了
    }

    const lat1 = 90;
    const lat2 = -90;
    const lon1 = -180;
    const lon2 = 180;
    const geometry = this.createTileGeometry(lat1, lat2, lon1, lon2);
    const material = new THREE.MeshBasicMaterial({ map: texture });
    const mesh = new THREE.Mesh(geometry, material);

    if (this.earthMesh) {
      this.earthMesh.add(mesh);
      this.level1TileMesh = mesh;
    }
  }

  // 清理纹理缓存
  public clearTextureCache() {
    // 保留level1的纹理，确保地球始终有底图
    const level1TextureKey = "1_1_1";
    const level1Texture = this.textureCache.get(level1TextureKey);

    // 清理除level1之外的所有纹理
    for (const [key, texture] of this.textureCache.entries()) {
      if (key !== level1TextureKey) {
        texture.dispose();
      }
    }

    // 重新设置缓存，只保留level1纹理
    this.textureCache.clear();
    if (level1Texture) {
      this.textureCache.set(level1TextureKey, level1Texture);
    }
    
    // 清理失败缓存，允许重新尝试加载失败的瓦片
    this.failedTileCache.clear();
    console.log("已清理失败瓦片缓存，将重新尝试加载失败的瓦片");
  }

  // 显示选区坐标信息
  private displaySelectionCoordinates() {
    if (!this.selectionPoints || this.selectionPoints.length !== 2) {
      return;
    }
    const [p1, p2] = this.selectionPoints;
    const lngRange = getCorrectLngRange(p1.lng, p2.lng);
    const rect = {
      minLat: Math.min(p1.lat, p2.lat),
      maxLat: Math.max(p1.lat, p2.lat),
      minLng: lngRange.minLng,
      maxLng: lngRange.maxLng,
    };

    // 通过onSelectionChange回调传递选区坐标信息
    if (this.onSelectionChange) {
      this.onSelectionChange(rect);
    }
  }

  // 隐藏选区坐标显示
  private hideSelectionCoordinates() {
    // 通过onSelectionChange回调传递null值来清除选区显示
    if (this.onSelectionChange) {
      this.onSelectionChange(null);
    }
  }

  // 进入选区模式
  public enterSelectionMode() {
    // 直接使用Meta键选区的逻辑，不需要额外的状态
  }

  // 退出选区模式
  public exitSelectionMode() {
    // 直接使用Meta键选区的逻辑，不需要额外的状态
  }

  // 设置模拟Meta键状态
  public setSimulatedMetaKey(simulated: boolean) {
    this.simulatedMetaKey = simulated;
    this.updateCursorStyle();
  }

  public clearSelection() {
    // 清空选区状态
    this.selectionPoints = null;
    this.selectionActive = false;
    this.selectionClickCount = 0;

    // 移除选区线
    if (this.selectionLine) {
      this.scene.remove(this.selectionLine);
      this.selectionLine.geometry.dispose();
      if (Array.isArray(this.selectionLine.material)) {
        this.selectionLine.material.forEach((material) => material.dispose());
      } else {
        this.selectionLine.material.dispose();
      }
      this.selectionLine = null;
    }

    // 清除相交状态
    this.intersectedPolygons = new Array(this.polygons.length).fill(false);
    this.intersectedDetectionPoints = new Array(this.detectionPoints.length).fill(false);

    // 更新高亮显示
    this.updateIntersectedPolygons();
    this.updateDetectionPointHighlights();

    // 通知父组件选区已清空
    this.onSelectionChange?.(null);
    this.onSelectionEnd?.(null, [], []);
  }
}

// Bing Maps quadkey算法
function tileXYToQuadKey(x: number, y: number, z: number): string {
  let quadKey = "";
  for (let i = z; i > 0; i--) {
    let digit = 0;
    const mask = 1 << (i - 1);
    if ((x & mask) !== 0) {
      digit += 1;
    }
    if ((y & mask) !== 0) {
      digit += 2;
    }
    quadKey += digit.toString();
  }
  return quadKey;
}
